/*
 * D-Bus Java Implementation Copyright (c) 2005-2006 Matthew Johnson This
 * program is free software; you can redistribute it and/or modify it under the
 * terms of either the GNU Lesser General Public License Version 2 or the
 * Academic Free Licence Version 2.1. Full licence texts are included in the
 * COPYING file with this program.
 */
package org.freedesktop.dbus;

import static org.freedesktop.dbus.Gettext._;

import java.util.HashMap;

import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;

public class DBusMatchRule {
  private static HashMap<String, Class<? extends DBusSignal>> signalTypeMap = new HashMap<String, Class<? extends DBusSignal>>();

  static Class<? extends DBusSignal> getCachedSignalType(String type) {
    return signalTypeMap.get(type);
  }

  private String iface;
  private String member;
  private String object;
  private String source;
  /* signal, error, method_call, method_reply */
  private String type;

  public DBusMatchRule(Class<? extends DBusInterface> c, String method) throws DBusException {
    this(c);
    member = method;
    type = "method_call";
  }

  @SuppressWarnings("unchecked")
  public DBusMatchRule(Class<? extends Object> c) throws DBusException {
    if (DBusInterface.class.isAssignableFrom(c)) {
      if (null != c.getAnnotation(DBusInterfaceName.class)) {
        iface = c.getAnnotation(DBusInterfaceName.class).value();
      } else {
        iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll(".");
      }
      if (!iface.matches(".*\\..*")) { throw new DBusException(_("DBusInterfaces must be defined in a package.")); }
      member = null;
      type = null;
    } else if (DBusSignal.class.isAssignableFrom(c)) {
      if (null == c.getEnclosingClass()) {
        throw new DBusException(
            _("Signals must be declared as a member of a class implementing DBusInterface which is the member of a package."));
      } else if (null != c.getEnclosingClass().getAnnotation(DBusInterfaceName.class)) {
        iface = c.getEnclosingClass().getAnnotation(DBusInterfaceName.class).value();
      } else {
        iface = AbstractConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll(".");
      }
      // Don't export things which are invalid D-Bus interfaces
      if (!iface.matches(".*\\..*")) { throw new DBusException(_("DBusInterfaces must be defined in a package.")); }
      if (c.isAnnotationPresent(DBusMemberName.class)) {
        member = c.getAnnotation(DBusMemberName.class).value();
      } else {
        member = c.getSimpleName();
      }
      signalTypeMap.put(iface + '$' + member, (Class<? extends DBusSignal>) c);
      type = "signal";
    } else if (Error.class.isAssignableFrom(c)) {
      if (null != c.getAnnotation(DBusInterfaceName.class)) {
        iface = c.getAnnotation(DBusInterfaceName.class).value();
      } else {
        iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll(".");
      }
      if (!iface.matches(".*\\..*")) { throw new DBusException(_("DBusInterfaces must be defined in a package.")); }
      member = null;
      type = "error";
    } else if (DBusExecutionException.class.isAssignableFrom(c)) {
      if (null != c.getClass().getAnnotation(DBusInterfaceName.class)) {
        iface = c.getClass().getAnnotation(DBusInterfaceName.class).value();
      } else {
        iface = AbstractConnection.dollar_pattern.matcher(c.getClass().getName()).replaceAll(".");
      }
      if (!iface.matches(".*\\..*")) { throw new DBusException(_("DBusInterfaces must be defined in a package.")); }
      member = null;
      type = "error";
    } else {
      throw new DBusException(_("Invalid type for match rule: ") + c);
    }
  }

  public DBusMatchRule(Class<? extends Object> c, String source, String object) throws DBusException {
    this(c);
    this.source = source;
    this.object = object;
  }

  public DBusMatchRule(DBusExecutionException e) throws DBusException {
    this(e.getClass());
    member = null;
    type = "error";
  }

  public DBusMatchRule(Message m) {
    iface = m.getInterface();
    member = m.getName();
    if (m instanceof DBusSignal) {
      type = "signal";
    } else if (m instanceof Error) {
      type = "error";
      member = null;
    } else if (m instanceof MethodCall) {
      type = "method_call";
    } else if (m instanceof MethodReturn) {
      type = "method_reply";
    }
  }

  public DBusMatchRule(String type, String iface, String member) {
    this.type = type;
    this.iface = iface;
    this.member = member;
  }

  public String getInterface() {
    return iface;
  }

  public String getMember() {
    return member;
  }

  public String getObject() {
    return object;
  }

  public String getSource() {
    return source;
  }

  public String getType() {
    return type;
  }

  @Override
  public String toString() {
    String s = null;
    if (null != type) {
      s = null == s ? "type='" + type + "'" : s + ",type='" + type + "'";
    }
    if (null != member) {
      s = null == s ? "member='" + member + "'" : s + ",member='" + member + "'";
    }
    if (null != iface) {
      s = null == s ? "interface='" + iface + "'" : s + ",interface='" + iface + "'";
    }
    if (null != source) {
      s = null == s ? "sender='" + source + "'" : s + ",sender='" + source + "'";
    }
    if (null != object) {
      s = null == s ? "path='" + object + "'" : s + ",path='" + object + "'";
    }
    return s;
  }

}
